package com.gitee.yongzhuzl.commonutil.util.http;

import com.gitee.yongzhuzl.commonutil.util.empty.EmptyUtil;
import org.apache.commons.io.IOUtils;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * description: HttpClient工具类，客户端用于调用服务端的服务
 * @author 朱正磊
 * @date 2017-05-23 11:11:41
 */
public class HttpClientUtil {

    private static final Logger log = LoggerFactory.getLogger(HttpClientUtil.class);

    /**
     * 温馨提示：默认采用UTF-8编码
     * description: 请求调用get类型服务接口含请求头含参
     * @author 朱正磊
     * @date 2025-09-02 15:35:42
     * @param url 请求服务接口地址
     * @param header 请求头信息
     * @param param 请求参数（url参数）
     * @return 返回服务接口响应的数据
     */
    public static String doGet(String url, Map<String, String> header, Map<String, String> param) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 创建uri并且传递参数
            URIBuilder builder = new URIBuilder(url);
            if (EmptyUtil.isNotEmpty(param)) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }

            URI uri = builder.build();

            // 创建基于http协议的get请求
            HttpGet httpGet = new HttpGet(uri);

            // 设置请求头信息
            if (EmptyUtil.isNotEmpty(header)) {
                for (String key : header.keySet()) {
                    httpGet.setHeader(key, header.get(key));
                }
            }

            // 执行请求
            return httpClient.execute(httpGet,
                    response -> EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
        } catch (IOException | URISyntaxException e) {
            log.error("远程调用http接口[{}]异常：", url, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * description: 请求调用get类型服务接口不含请求头含参
     * @author 朱正磊
     * @date 2025-09-02 15:36:27
     * @param url 请求服务接口地址
     * @param param 请求参数（url参数）
     * @return 返回服务接口响应的数据
     */
    public static String doGet(String url, Map<String, String> param) {
        return doGet(url, null, param);
    }

    /**
     * description: 请求调用get类型服务接口含请求头不含参
     * @author 朱正磊
     * @date 2025-09-02 15:36:35
     * @param header 请求头信息
     * @param url 请求服务接口地址
     * @return 返回服务接口响应的数据
     */
    public static String doGet(Map<String, String> header, String url) {
        return doGet(url, header, null);
    }

    /**
     * description: 请求调用get类型服务接口不含请求头不含参
     * @author 朱正磊
     * @date 2025-09-02 15:36:40
     * @param url 请求服务接口地址
     * @return 返回服务接口响应的数据
     */
    public static String doGet(String url) {
        return doGet(url, null, null);
    }

    /**
     * 温馨提示：请求数据类型为Content-Type: application/x-www-form-urlencoded; charset=UTF-8，默认采用UTF-8编码
     * description: 请求调用post类型服务接口含请求头含参
     * @author 朱正磊
     * @date 2025-09-02 15:36:46
     * @param url 请求服务接口地址
     * @param header 请求头信息
     * @param param 请求参数（请求体内容）
     * @return 返回服务接口响应的数据
     */
    public static String doPost(String url, Map<String, String> header, Map<String, String> param) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            //创建基于http协议的post请求
            HttpPost httpPost = new HttpPost(url);

            // 设置请求头信息
            if (EmptyUtil.isNotEmpty(header)) {
                for (String key : header.keySet()) {
                    httpPost.setHeader(key, header.get(key));
                }
            }

            // 创建参数列表，模拟表单提交数据
            if (EmptyUtil.isNotEmpty(param)) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (String key : param.keySet()) {
                    paramList.add(new BasicNameValuePair(key, param.get(key)));
                }
                StringEntity entity = new UrlEncodedFormEntity(paramList, StandardCharsets.UTF_8);
                httpPost.setEntity(entity);
            }

            // 执行请求
            return httpClient.execute(httpPost,
                    response -> EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
        } catch (IOException e) {
            log.error("远程调用http接口[{}]异常：", url, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * description: 请求调用post类型服务接口不含请求头含参
     * @author 朱正磊
     * @date 2025-09-02 15:36:52
     * @param url 请求服务接口地址
     * @param param 请求参数（请求体内容）
     * @return 返回服务接口响应的数据
     */
    public static String doPost(String url, Map<String, String> param) {
        return doPost(url, null, param);
    }

    /**
     * description: 请求调用post类型服务接口含请求头不含参
     * @author 朱正磊
     * @date 2025-09-02 15:36:58
     * @param header 请求头信息
     * @param url 请求服务接口地址
     * @return 返回服务接口响应的数据
     */
    public static String doPost(Map<String, String> header, String url) {
        return doPost(url, header, null);
    }

    /**
     * description: 请求调用post类型服务接口不含请求头不含参
     * @author 朱正磊
     * @date 2025-09-02 15:37:03
     * @param url 请求服务接口地址
     * @return 返回服务接口响应的数据
     */
    public static String doPost(String url) {
        return doPost(url, null, null);
    }

    /**
     * 温馨提示：请求数据类型为Content-Type: application/json; charset=UTF-8，默认采用UTF-8编码
     * description: 请求参数是json字符串形式的post请求调用服务接口（含请求头）
     * @author 朱正磊
     * @date 2025-09-02 15:37:09
     * @param url 请求服务接口地址
     * @param header 请求头信息
     * @param json 请求参数是json字符串（请求体内容）
     * @return 返回服务接口响应的数据
     */
    public static String doPostJson(String url, Map<String, String> header, String json) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 创建基于http协议的post请求
            HttpPost httpPost = new HttpPost(url);

            // 设置请求头信息
            if (EmptyUtil.isNotEmpty(header)) {
                for (String key : header.keySet()) {
                    httpPost.setHeader(key, header.get(key));
                }
            }

            // 创建请求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);

            // 执行请求
            return httpClient.execute(httpPost,
                    response -> EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
        } catch (IOException e) {
            log.error("远程调用http接口[{}]异常：", url, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * description: 请求参数是json字符串形式的post请求调用服务接口（不含请求头）
     * @author 朱正磊
     * @date 2025-09-02 15:37:15
     * @param url 请求服务接口地址
     * @param json 请求参数是json字符串（请求体内容）
     * @return 返回服务接口响应的数据
     */
    public static String doPostJson(String url, String json) {
        return doPostJson(url, null, json);
    }

    /**
     * 温馨提示：请求数据类型为Content-Type: multipart/form-data，默认采用UTF-8编码
     * description: 发送HTTP的POST请求，支持文件上传（含请求头）
     * @author 朱正磊
     * @date 2025-09-02 15:37:21
     * @param url 请求url地址
     * @param params 类似form表单键值对（普通参数）（如果没有普通参数，实参传null）
     * @param filesMap 待上传的文件Map集合（二进制参数）（如果没有二进制参数，实参传null）
     * @param headers 请求头信息（如果没有请求头，实参传null）
     * @return 返回服务接口响应的数据
     */
    public static String doPostFormMultipart(String url, Map<String, String> params, Map<String, List<File>> filesMap, Map<String, String> headers) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httPost = new HttpPost(url);

            // 设置header
            if (EmptyUtil.isNotEmpty(headers)) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httPost.setHeader(entry.getKey(), entry.getValue());
                }
            }

            // 创建请求体
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            // 设置模式，防止传输附件到第三方系统出现文件名乱码，支持中文
            builder.setMode(HttpMultipartMode.EXTENDED);
            builder.setCharset(StandardCharsets.UTF_8);

            // 添加普通参数
            // 解决中文乱码
            ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
            if (EmptyUtil.isNotEmpty(params)) {
                Set<String> keySet = params.keySet();
                for (String key : keySet) {
                    builder.addTextBody(key, params.get(key), contentType);
                }
            }

            // 添加二进制参数
            if (EmptyUtil.isNotEmpty(filesMap)) {
                for (Map.Entry<String, List<File>> entry: filesMap.entrySet()) {
                    String key = entry.getKey();
                    List<File> files = entry.getValue();
                    for (File file : files) {
                        builder.addBinaryBody(key, file);
                    }
                }
            }

            // 设置请求体
            httPost.setEntity(builder.build());

            // 执行请求
            return httpClient.execute(httPost,
                    response -> EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
        } catch (IOException e) {
            log.error("远程调用http接口[{}]异常：", url, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * description: 发送HTTP的POST请求，支持文件上传（不含请求头）
     * @author 朱正磊
     * @date 2025-09-02 15:37:28
     * @param url 请求url地址
     * @param params 类似form表单键值对（普通参数）（如果没有普通参数，实参传null）
     * @param filesMap 待上传的文件Map集合（二进制参数）（如果没有二进制参数，实参传null）
     * @return 返回服务接口响应的数据
     */
    public static String doPostFormMultipart(String url, Map<String, String> params, Map<String, List<File>> filesMap) {
        return doPostFormMultipart(url, params, filesMap, null);
    }

    /**
     * description: 请求参数是xml格式的post请求调用服务接口
     * @author 朱正磊
     * @date 2025-09-02 15:37:34
     * @param url 请求地址
     * @param xmlData xml格式请求参数
     * @return 返回服务接口响应的数据
     */
    public static String doPostXml(String url, String xmlData) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 创建HTTP POST请求对象
            HttpPost httpPost = new HttpPost(url);

            // 设置请求头（指明请求内容类型以及字符编码）
            httpPost.setHeader("Content-Type", "text/xml; charset=UTF-8");

            // 创建请求体（指明字符编码）
            StringEntity entity = new StringEntity(xmlData, StandardCharsets.UTF_8);

            // 设置请求体
            httpPost.setEntity(entity);

            // 执行请求
            return httpClient.execute(httpPost,
                    response -> EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
        } catch (IOException e) {
            log.error("远程调用http接口[{}]异常：", url, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 温馨提醒：URL解码操作最好放在具体业务里面进行
     * 强调：返回值与请求数据格式一模一样保持不变，比如name=张三&age=10，{"name":"张三","age":10}
     * 说明：这是服务端调用的方法（支持POST请求不支持GET请求）
     * description: 获取客户端请求的数据内容
     * @author 朱正磊
     * @date 2025-09-02 15:37:41
     * @param request 请求对象
     * @return 返回客户端请求的数据
     */
    public static String getRequestContent(HttpServletRequest request) {
        try {
            StringBuilder stringBuilder = new StringBuilder();
            BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
            String line ;
            while ((line = in.readLine()) != null) {
                stringBuilder.append(line);
            }
            in.close();
            return stringBuilder.toString();
        } catch (IOException e) {
            log.error("获取客户端请求的数据内容异常：", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 强调：该方法不支持GET请求，支持POST请求、PUT请求
     * 说明：如果是POST请求（Content-Type=application/x-www-form-urlencoded），Servlet先调用了getParameter系列方法，
     * 那么再调用该方法无效，PUT请求没有该问题。
     * description: 获取客户端请求的请求体数据
     * @author 朱正磊
     * @date 2025-09-02 15:37:48
     * @param request 请求对象
     * @return 返回客户端请求的数据
     */
    public static String getRequestBodyData(HttpServletRequest request) {
        try {
            return IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            log.error("获取客户端请求的请求体数据异常：", e);
            throw new RuntimeException(e);
        }
    }

}
